//你好
const { def, base, intField, setEvent, regFieldGetSet, regEvent } = require("./jsdemo_windows.common.cpp.js");

const form_event = ["onClose"];
const form = String.raw/* cpp */`
  class Form : public Window{
    public:
    Form();
    ~Form();

    void render(int mask);
    void mouseDown(int x, int y, int e);
    void mouseUp(int x, int y, int e);
    void mouseMove(int x, int y, int e);
    void keyEvent(int key, int chr);

    char* title;
    int textX;
    int textY;
    int draging;
    int startX;
    int startY;
    Button* buttonClose;
    JSValue buttonClose_js_obj;
    Window* mouseDownWindow;
    Window* focusWindow;
    ArrayList* children;

    void setWindows(Windows* windows);
${form_event.map((event)=>`
    JSValue ${event};
`).join("")}

  };

  
  static JSValue js_form_buttonClose_click(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
    Button *p = (Button*)JS_GetOpaque(this_val);
    Form* form = (Form*)p->tag;
    
    if(form->onClose.tag!=JS_TAG_UNDEFINED){
        JSValue ret = JS_Call(ctx, form->onClose, form->js_obj, 0, 0);
        if (JS_IsException(ret)) {
            dump_exception(ctx, JS_GetException(ctx));
        }
        JS_FreeValue(ctx, ret);
    }
    return JS_UNDEFINED;
  }

  Form::Form() : Window() {
    title = 0;
    width = 100;
    height = 26;
    textX = 5;
    textY = 5;
    draging = 0;
    mouseDownWindow = 0;
    focusWindow = 0;
    id = 124;

    buttonClose_js_obj = js_button_ctor(ctx, JS_UNDEFINED, 0, 0);
    buttonClose = (Button*)JS_GetOpaque(buttonClose_js_obj);
    
    //buttonClose->id = 333;

    // char* title = (char*)malloc(3);
    // title[0] = 0xA1;
    // title[1] = 0xC1;
    // title[2] = 0;
    buttonClose->title = 0;//title;

    JSValue onClick = JS_NewCFunction(ctx, js_form_buttonClose_click, "click", 0);
    buttonClose->onClick = onClick;
    buttonClose->tag = this;
    buttonClose->top = 2;
    buttonClose->width = 26;

    children = new ArrayList();

${form_event.map((event)=> `
    ${event}.tag = JS_TAG_UNDEFINED;
`).join("")}


  }


  Form::~Form(){
    printf("~Form\n");
    if(title){
      free(title);
    }
    
    JS_FreeValue(ctx, buttonClose_js_obj);

    for(int i = 0; i< children->length;i++){
      Window* win = (Window*)children->get(i);
      JS_FreeValue(ctx, win->js_obj);
    }
    delete children;

${form_event.map((event)=>`
    JS_FreeValue(ctx, ${event});
`).join("")}

  }

  void Form::render(int mask){
    if(needRefresh){
      if((beforeRender.tag & mask)!=JS_TAG_UNDEFINED){
        jsCall(beforeRender);
      }
        
      Window::render(0);
      
      int shade = getRGB565(0x808080);
      int bright = getRGB565(0xFFFFFF);
      int bg = getRGB565(bg_color);

      int top = 0;
      if(windows){
        top = this == windows->topWindow;
      }

      paint_pattern(abs_pos_x            , abs_pos_y             ,     2, height, (shade<<16) | bright, top ? 0 : 0x11112222);
      paint_pattern(abs_pos_x            , abs_pos_y             , width,      2, (shade<<16) | bright, top ? 0 : 0xAAAA5555);
      paint_pattern(abs_pos_x + width - 2, abs_pos_y             ,     2, height,(bright<<16) |  shade, top ? 0 : 0x11112222);
      paint_pattern(abs_pos_x            , abs_pos_y + height - 2, width,      2,(bright<<16) |  shade, top ? 0 : 0xAAAA5555);
      paint_pattern(abs_pos_x + 2        , abs_pos_y + 2         , width-4, height-4, bg, 0);
      
      if(title){
        FILE* s = getStream(textX, textY, width, 20);
        fprintf(s, title);
      }
      if((afterRender.tag & mask)!=JS_TAG_UNDEFINED){
        jsCall(afterRender);
      }
    }

    if(needRefresh){
      buttonClose->needRefresh = 1;
    }
    buttonClose->baseX = abs_pos_x;
    buttonClose->baseY = abs_pos_y;
    buttonClose->left = width - 26 - 2;
    buttonClose->render(0xFFFF);
    {
      int ctable[] = {
        0x00000000,// ctable: 0,  00000000
        0x00000000,// ctable: 2,  00000000
        0x08201010,// ctable: 4,  08201010
        0x02800440,// ctable: 6,  02800440
        0x02800100,// ctable: 8,  02800100
        0x08200440,// ctable: 10, 08200440
        0x00001010,// ctable: 12, 00001010
        0x00000000,// ctable: 14, 00000000
      };
      
      Window* win = buttonClose;
      int fg = getRGB565(win->fg_color);
      int bg = getRGB565(win->bg_color);
      int color = (fg<<16) | bg;
      int x=5;
      int y=5;
      int* p = ctable;
      IOWR(VGA_BLT, BLT_M_RST_FIFO, 0);
      IOWR(VGA_BLT, BLT_S_WIDTH_HEIGHT, 16); // w
      IOWR(VGA_BLT, BLT_S_WIDTH_HEIGHT, 2); // h
      IOWR(VGA_BLT, BLT_S_SETVAL, color);
      IOWR(VGA_BLT, BLT_S_DESTX, x + win->abs_pos_x);
      
      if(! ((x + win->abs_pos_x) < 512 && (x + win->abs_pos_x) > (512 - 8))){
        // for(j=0;j<16;j+=4){
        //   int calc_y = (*y + j);//*1024;
        //   //paint_pattern(*x + win->abs_pos_x, calc_y + win->abs_pos_y, 8, 4, color, *p++);
        //   IOWR(VGA_BLT, BLT_S_PATTERN, *p++);
        //   IOWR(VGA_BLT, BLT_M_DESTY_MEMSET_REQ, calc_y + win->abs_pos_y);
        // }
        for(int j=0;j<16;j+=2){
          int calc_y = (y + j);
          //paint_pattern(*x + win->abs_pos_x, calc_y + win->abs_pos_y, 16, 2, color, *p++);
          IOWR(VGA_BLT, BLT_S_PATTERN, *p++);
          IOWR(VGA_BLT, BLT_M_DESTY_MEMSET_REQ, calc_y + win->abs_pos_y);
        }
      }else{
        // IOWR(VGA_BLT, BLT_M_RST_FIFO, 0);
        // IOWR(VGA_BLT, BLT_S_WIDTH_HEIGHT, 8);
        // IOWR(VGA_BLT, BLT_S_WIDTH_HEIGHT, 16);
        // //IOWR(VGA_BLT, BLT_S_DESTX, (basexy >> 1) & 1023);//paint_pattern(abs_pos_x+x, abs_pos_y+y, w, h, color, pattern);
        // for(int b=0;b<16;b+=4){
        //   int pat = *p++;
        //   for(int c=0;c<4;c++){
        //     //int calc_y = (y + b + c);//*1024;
        //     for(int a=0;a<8;a++){
        //       IOWR(VGA_BLT, BLT_M_WRITE_DATA_YY, (pat&1)?fg:bg);
        //       pat = pat >> 1;
        //     }
        //   }
        // }
        // IOWR(VGA_BLT, BLT_M_DESTY_BUFWRI_REQ, *y + win->abs_pos_y);

        // IOWR(VGA_BLT, BLT_S_WIDTH_HEIGHT, 8); // w
        // IOWR(VGA_BLT, BLT_S_WIDTH_HEIGHT, 4); // h

        IOWR(VGA_BLT, BLT_M_RST_FIFO, 0);
        IOWR(VGA_BLT, BLT_S_WIDTH_HEIGHT, 16);
        IOWR(VGA_BLT, BLT_S_WIDTH_HEIGHT, 16);
        //IOWR(VGA_BLT, BLT_S_DESTX, (basexy >> 1) & 1023);//paint_pattern(abs_pos_x+x, abs_pos_y+y, w, h, color, pattern);
        for(int b=0;b<16;b+=2){
          int pat = *p++;
          for(int c=0;c<2;c++){
            //int calc_y = (y + b + c);//*1024;
            for(int a=0;a<16;a++){
              IOWR(VGA_BLT, BLT_M_WRITE_DATA_YY, (pat&1)?fg:bg);
              pat = pat >> 1;
            }
          }
        }
        IOWR(VGA_BLT, BLT_M_DESTY_BUFWRI_REQ, y + win->abs_pos_y);        
      }
    }
    for(int i = 0; i<children->length;i++){
      Window* win = (Window*)children->get(i);
      if(needRefresh){
        win->needRefresh = 1;
      }
      win->baseX = abs_pos_x;
      win->baseY = abs_pos_y;
      win->render(0xFFFF);
      if((needRefresh)){
        if(focusWindow==win){
          int x = win->left + abs_pos_x - 4;
          int y = win->top + abs_pos_y - 4;
          paint_pattern(x                 , y                  ,              2, win->height + 6, getRGB565(0x0000FF), 0);
          paint_pattern(x                 , y                  , win->width + 6,               2, getRGB565(0x0000FF), 0);
          paint_pattern(x + win->width + 6, y                  ,              2, win->height + 6, getRGB565(0x0000FF), 0);
          paint_pattern(x                 , y + win->height + 6, win->width + 8,               2, getRGB565(0x0000FF), 0);
        }
      }
    }

    if(mask){
      needRefresh = 0;
    }
  }

  void Form::mouseDown(int x, int y, int e){
    if(x>2&&y>2 && x<width-26-2 && y < 26){
      draging = 1;
      startX = x; 
      startY = y;
    }else{
      int target_x = buttonClose->left;
      int target_y = buttonClose->top;
      int target_w = buttonClose->width;
      int target_h = buttonClose->height;
      if(x>=target_x && x<target_x+target_w && y>=target_y && y<target_y+target_h){
        buttonClose->mouseDown(x - target_x, y - target_y, e);
        mouseDownWindow = buttonClose;
      }else{

        for(int i = 0; i< children->length;i++){
          Window* win = (Window*)children->get(i);
          target_x = win->left;
          target_y = win->top;
          target_w = win->width;
          target_h = win->height;
          if(x>=target_x && x<target_x+target_w && y>=target_y && y<target_y+target_h){
            win->mouseDown(x - target_x, y - target_y, e);
            mouseDownWindow = win;
            break;
          }
        }
      }  
    }
  }
  void Form::mouseUp(int x, int y, int e){
    if(draging){
      draging = 0;
      left += x - startX;
      top += y - startY;
      windows->needRefresh = 1;
    }
    if(mouseDownWindow){
      int target_x = mouseDownWindow->left;
      int target_y = mouseDownWindow->top;
      int target_w = mouseDownWindow->width;
      int target_h = mouseDownWindow->height;
      mouseDownWindow->mouseUp(x - target_x, y - target_y, e);
      if(focusWindow != mouseDownWindow){
        focusWindow = mouseDownWindow;
        needRefresh = 1;
      }
      mouseDownWindow = 0;

    }
  }
  void Form::mouseMove(int x, int y, int e){
    int target_x;
    int target_y;
    int target_w;
    int target_h;
    for(int i = 0; i< children->length;i++){
      Window* win = (Window*)children->get(i);
      target_x = win->left;
      target_y = win->top;
      target_w = win->width;
      target_h = win->height;
      if(x>=target_x && x<target_x+target_w && y>=target_y && y<target_y+target_h){
        win->mouseMove(x, y, e);
        break;
      }
    }
  }
  void Form::keyEvent(int key, int chr){
    //下发
    //焦点
    if(focusWindow){
      focusWindow->keyEvent(key, chr);
    }
  }
  void Form::setWindows(Windows* windows){
    this->windows = windows;
    buttonClose->windows = windows;
    for(int i = 0; i< children->length;i++){
      Window* win = (Window*)children->get(i);
      win->windows = windows;
    }
  }
  

  static JSValue js_form_addChild(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
    Form *form = (Form*)JS_GetOpaque(this_val);
    if (!JS_IsInstanceOf(ctx, argv[0], JS_GetPropertyStr(ctx, global, "Window"))) {
      return JS_UNDEFINED;
    }
    Window *window = (Window*)JS_GetOpaque(argv[0]);
    if(form && window){
      JS_DupValue(ctx, argv[0]);
      form->children->push(window);
      window->setWindows(form->windows);
    }
    return JS_UNDEFINED;
  }

  static JSValue js_form_removeChild(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
    Form *form = (Form*)JS_GetOpaque(this_val);
    if (!JS_IsInstanceOf(ctx, argv[0], JS_GetPropertyStr(ctx, global, "Window"))) {
      return JS_UNDEFINED;
    }
    Window *window = (Window*)JS_GetOpaque(argv[0]);
    JS_FreeValue(ctx, argv[0]);
    form->children->remove(window);
    return JS_UNDEFINED;
  }

  static JSValue js_form_setFocus(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
    Form *form = (Form*)JS_GetOpaque(this_val);
    if (!JS_IsInstanceOf(ctx, argv[0], JS_GetPropertyStr(ctx, global, "Window"))) {
      return JS_UNDEFINED;
    }
    Window *window = (Window*)JS_GetOpaque(argv[0]);
    form->focusWindow = window;
    form->needRefresh = 1;
    return JS_UNDEFINED;
  }
  

  ${form_event.map((event)=>{
    return setEvent("Form", "form", event);
  }).join("")}
  
  static JSValue js_form_ctor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv) {
    Form* p = new Form();
    JSValue obj = JS_NewObjectClass(ctx, js_form_class_id);
    p->js_obj = obj;
    JS_SetOpaque(obj, p);
    return obj;
  }
  static void js_form_finalizer(JSRuntime *rt, JSValue val) {
    Form *p = (Form*)JS_GetOpaque(val);
    if(p){
      delete p;
    }
  }

  static JSValue js_form_set_title(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
    Form *p = (Form*)JS_GetOpaque(this_val);
    if(p){
      if(p->title){
        free(p->title);
      }
      size_t len; char* str = (char*)JS_ToCStringLen(ctx, &len, argv[0]);
      p->title = (char*)malloc(len+1);
      memcpy(p->title, str, len+1);
      JS_FreeCString(ctx, str);
      p->needRefresh = 1;
    }
    return JS_UNDEFINED;
  }

  void form_init(JSValue* window_ctor, JSValue* window_proto) {
    JS_NewClassID(&js_form_class_id);
    JSClassDef form_def = {
      .class_name = "Form",
      .finalizer = js_form_finalizer,
    };
    JS_NewClass(rt, js_form_class_id, &form_def);

    JSValue form_proto = JS_NewObject(ctx);

    // 添加 getter
    JS_DefinePropertyGetSet(ctx, form_proto, JS_NewAtom(ctx, "title"), JS_UNDEFINED, JS_NewCFunction(ctx, js_form_set_title, "title", 1), JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE);

    JS_SetPropertyStr(ctx, form_proto, "addChild", JS_NewCFunction(ctx, js_form_addChild, "addChild", 1));
    JS_SetPropertyStr(ctx, form_proto, "removeChild", JS_NewCFunction(ctx, js_form_removeChild, "removeChild", 1));
    JS_SetPropertyStr(ctx, form_proto, "setFocus", JS_NewCFunction(ctx, js_form_setFocus, "setFocus", 1));

    JS_SetClassProto(ctx, js_form_class_id, form_proto);

${form_event.map((event)=>{
  return regEvent("form", "form_proto", event);
}).join("")}

    JSValue form_ctor = JS_NewCFunction2(ctx, js_form_ctor, "Form", 2, JS_CFUNC_constructor, 0);
    JS_SetConstructor(ctx, form_ctor, form_proto);
    JS_SetPropertyStr(ctx, global, "Form", form_ctor);

    JS_SetPrototype(ctx, form_proto, *window_proto);
    JS_SetPrototype(ctx, form_ctor, *window_ctor);
  }  
`;

module.exports = { form };

